// ==UserScript==
// @name         区块链客户端（旧）
// @version      2.0
// @description  区块链客户端（旧）
// @author       fengtian
// @match        https://eege7p.pai4sh.com/*
// @match        https://114.111.61.102/*
// @grant        GM_xmlhttpRequest
// @connect      192.168.0.254
// @connect      127.0.0.1
// @connect      eege7p.pai4sh.com
// @connect      114.111.61.102
// ==/UserScript==

(function () {
    'use strict';
    
    let domain = "http://192.168.0.254:8888";
    let remote_websocket_domain = "ws://192.168.0.254:18765";
    let local_web_server = "http://127.0.0.1:23668";
    let local_websocket_domain = "wss://localhost:23443";

    function main_func(){

        // ---------------------启动websocket，监听跟单信息，自动跟单--------------------------
        (function() {
            const WEBSOCKET_URL = `${local_websocket_domain}/ws/${Username}`; // 替换为你的地址
            let socket = null;
            let reconnectAttempts = 0;
            const maxReconnectAttempts = 10;
            const baseReconnectDelay = 2000;

            function connectWebSocket() {
                socket = new WebSocket(WEBSOCKET_URL);

                socket.onopen = () => {
                    console.log("[WebSocket] 已连接", WEBSOCKET_URL);
                    reconnectAttempts = 0;
                };

                socket.onmessage = (event) => {
                    console.log(1111111,  event.data);

                    let Token = "";
                        const userInfoRaw = localStorage.getItem('kr/kv/userInfo');
                        if (userInfoRaw) {
                
                            const userInfo = JSON.parse(JSON.parse(userInfoRaw));
                            Token = userInfo["token"];
                        }

                    GM_xmlhttpRequest({
                        method: "POST",
                        url: `${window.location.origin}/APIV2/GraphQL?l=en&pf=web`,
                        headers: {
                            'Accept': 'application/json, text/plain, */*',
                            'Accept-Language': 'en,zh-CN;q=0.9,zh;q=0.8',
                            'Authorization': Token,
                            'Connection': 'keep-alive',
                            'Content-Type': 'application/json',
                            'Origin': window.location.origin,
                            'Referer': window.location.href,
                            'Sec-Fetch-Dest': 'empty',
                            'Sec-Fetch-Mode': 'cors',
                            'Sec-Fetch-Site': 'same-origin',
                            'User-Agent': navigator.userAgent,
                        },
                        data: event.data,
                        onload: function (response) {
                            try {
                                const result = JSON.parse(response.response);

                                const message = result?.data?.AddLotteryOrders?.message;

                                if (!message?.includes("投注成功")) {
                                    console.log("[WebSocket] 跟单失败", response);
                                    alert(`[WebSocket] 跟单失败 ${result.errors[0].message}`);
                                    return;
                                }
                                
                                console.log("[WebSocket] 跟单成功", response);

                            } catch (e) {
                                console.error("[WebSocket] 跟单解析失败：", e);
                                alert("[WebSocket] 跟单解析失败");
                            }
                        },
                        onerror: function (error) {
                            console.error("[WebSocket] 跟单请求失败：", error);
                            alert("[WebSocket] 跟单请求失败");
                        }
                    }); 
                };

                socket.onerror = (error) => {
                    console.error("[WebSocket] 错误:", error);
                };

                socket.onclose = (event) => {
                    console.warn(`[WebSocket] 连接关闭（code: ${event.code}, reason: ${event.reason}）`);

                    if (reconnectAttempts < maxReconnectAttempts) {
                        const delay = baseReconnectDelay * Math.pow(2, reconnectAttempts);
                        reconnectAttempts++;
                        console.log(`[WebSocket] 第 ${reconnectAttempts} 次重连将在 ${delay / 1000} 秒后开始...`);
                        setTimeout(connectWebSocket, delay);
                    } else {
                        console.error("[WebSocket] 超过最大重连次数，停止尝试");
                    }
                };
            }

            connectWebSocket();
        })();

        // ---------------------监听投注时间, 上传投注信息--------------------------
        (function() {

            const unitMap = {
                '元': 1,
                '角': 0.1,
                '分': 0.01,
                '厘': 0.001
            };

            let bet_info = {
                "func": "func_1_0"
            };

            const targetURL = '/APIV2/GraphQL';
        
            const originalOpen = XMLHttpRequest.prototype.open;
            const originalSend = XMLHttpRequest.prototype.send;
        
            XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
            this._method = method;
            this._url = url;
            return originalOpen.apply(this, arguments);
            };
        
            XMLHttpRequest.prototype.send = function(body) {
            const urlWithoutParams = this._url.split('?')[0];
        
            if (
                this._method === 'POST' &&
                urlWithoutParams.includes(targetURL) &&
                typeof body === 'string'
            ) {
                try {
                const parsedBody = JSON.parse(body);
                if (parsedBody.operationName === 'AddLotteryOrders') {
                    

                    const inputList = parsedBody.variables?.input || [];

                    for (let i = 0; i < inputList.length; i++) {
                        const item = inputList[i];
                        
                        const game_id = item["game_id"]; // 游戏id
                        const game_type_id = item["game_type_id"]; // 游戏玩法id
                        const bet_multiple = item["bet_multiple"]; // 下注倍数

                        // 金额单位 1元
                        const per_price_node = document.querySelectorAll('.lottery-tab-bar')[0]
                        if (per_price_node){
                            const activeDiv = per_price_node.querySelector('.qo-tab-tar-item.active');
                            if (activeDiv) {
                                const text = activeDiv.innerText.trim(); // 例：'2分'
                                const match = text.match(/^(\d+)([元角分厘])$/);

                                if (match) {
                                    const amount = parseInt(match[1], 10);
                                    const unit = match[2];
                                    const result = amount * unitMap[unit];
                                    bet_info["PerPrice"] = result.toString();
                                }
                            }
                        }

                        let Username = "";
                        const userInfoRaw = localStorage.getItem('kr/kv/userInfo');
                        if (userInfoRaw) {
                
                            const userInfo = JSON.parse(JSON.parse(userInfoRaw));
                            Username = userInfo["account"];
                        }

                        let BettingCount = 0;
                        const el = document.getElementById('titleNoteNumber2');
                        if (el) {
                            const text = el.textContent || el.innerText; // 获取文本内容
                            const match = text.match(/\d+/); // 匹配第一个数字
                        
                            if (match) {
                            BettingCount = parseInt(match[0], 10); // 转换为数字
                            }
                        }

                        const BettingNumber = Array.from(document.querySelectorAll('#numberContainer2 button.active'))
                            .map(btn => btn.textContent.trim())
                            .join('');
                        
                        const GenerateNumber = Array.from(document.querySelectorAll('#generateContainer2 button.active'))
                            .map(btn => btn.textContent.trim())
                            .join('');

                        bet_info["BettingNumber"] = BettingNumber;
                        bet_info["Multiple"] = bet_multiple;
                        bet_info["BettingCount"] = BettingCount;

                        bet_info["Username"] = Username;
                        bet_info["LOTTERY_CODE"] = game_id.toString();
                        bet_info["PlayCode"] = `${BettingNumber.length}_${GenerateNumber}`;
                        bet_info["LOTTERY_PLAY_DETAIL_CODE"] = `${game_id}_${game_type_id}`;

                        console.log("[区块链客户端] 投注内容", bet_info);
                        GM_xmlhttpRequest({
                            method: "POST",
                            url: `${domain}/only-follow-betting`,
                            headers: {
                                "Content-Type": "application/json",  
                                "Accept": "application/json", 
                            },
                            data: JSON.stringify(bet_info),
                            onload: function (response) {
                                try {
                                    const result = JSON.parse(response.responseText);

                                    if (!result.ok){
                                        console.log("[区块链客户端] 投注失败", result);
                                        alert("[区块链客户端] 正在开奖, 投注失败, 请撤单");
                                        return;
                                    }

                                    console.log("[区块链客户端] 投注成功", result);

                                } catch (e) {
                                    console.error("[区块链客户端] 投注解析失败：", e);
                                    alert("[区块链客户端] 投注解析失败");
                                }
                            },
                            onerror: function (error) {
                                console.error("[区块链客户端] 投注请求失败：", error);
                                alert("[区块链客户端] 投注请求失败");
                            }
                        }); 
                    }
                }
                } catch (err) {
                    console.error('解析 AddLotteryOrders 请求失败:', err);
                }
            }
        
            return originalSend.call(this, body);
            };
        })();

        class Lottery3DGenerator {
            static generateAllCombinations() {
                const combinations = [];
                for (let i = 0; i < 10; i++) {
                    for (let j = 0; j < 10; j++) {
                        for (let k = 0; k < 10; k++) {
                            combinations.push(`${i}${j}${k}`);
                        }
                    }
                }
                return combinations;
            }
        
            /**
             * 返回符合条件的号码组合数组
             * @param {number[]} selectedNumbers 选中的号码数组
             * @param {number} requiredCount 需要出现的次数
             * @returns {string[]} 符合条件的号码组合数组
             */
            static getCombinations(selectedNumbers, requiredCount) {
                const allCombinations = this.generateAllCombinations();
                const selectedSet = new Set(selectedNumbers);
                const result = [];
                for (const combo of allCombinations) {
                    let matchCount = 0;
                    for (const digit of combo) {
                        if (selectedSet.has(parseInt(digit))) {
                            matchCount++;
                        }
                    }
                    if (matchCount === requiredCount) {
                        result.push(combo);
                    }
                }
                
                return result;
            }
        }

        // ---------------------------替换顶部账号等级图片---------------------------
        // 图片替换逻辑封装成函数
        function replaceImages() {
            const imgs = document.querySelectorAll('.tnw-right img');
            if (imgs.length > 1) {
                imgs[1].src = "assets/images/icons/interest-rank/钛金级.png";
            }
        }

        // 先尝试直接执行（页面已加载或元素已存在的情况）
        replaceImages();

        // 使用 MutationObserver 监听元素出现
        const observer = new MutationObserver((mutationsList, obs) => {
            for (const mutation of mutationsList) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1) { // 是元素节点
                        if (node.matches('.tnw-right img') || node.querySelector?.('.tnw-right img')) {
                            replaceImages();
                        }
                    }
                }
            }
        });

        // 开始监听整个 body 的子树
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // ---------------------------定时器等待元素出现---------------------------
        function waitForElement(selector, timeout = 30000) {
            return new Promise((resolve, reject) => {
                const interval = 100;
                let elapsed = 0;
                const timer = setInterval(() => {
                    const element = document.querySelector(selector);
                    if (element) {
                        clearInterval(timer);
                        resolve(element);
                    }
                    elapsed += interval;
                    if (elapsed >= timeout) {
                        clearInterval(timer);
                        reject(new Error(`Timeout: Element "${selector}" not found.`));
                    }
                }, interval);
            });
        }

        // 等待主容器出现再初始化脚本
        waitForElement(".help-info.ng-star-inserted").then(() => {
            initFloatingPanel();
        }).catch((err) => {
            console.error(err.message);
        });

        // 绑定第一排号码的位置的上面
        function bind_window(floatDiv){
            const boxes = document.querySelectorAll(".help-info.ng-star-inserted");
            if (boxes.length > 0) {
                const targetBox = boxes[0];
                const rect = targetBox.getBoundingClientRect();
                floatDiv.style.position = 'fixed';
                floatDiv.style.left = (rect.left - 1) + 'px'; // 向左偏移
                // floatDiv.style.top = (rect.top - floatDiv.offsetHeight - 2) + 'px'; // 向上偏移
                floatDiv.style.top = (rect.top - floatDiv.offsetHeight + 28) + 'px'; // 向下偏移
                floatDiv.style.right = '';
                floatDiv.style.bottom = '';
                return;
            }
        }

        // 刷新注数计算结果
        function update_combinations(){
            
            const combinations = Lottery3DGenerator.getCombinations(window.number_set, window.generate_number);
            const textarea = document.querySelector('textarea')
            if (textarea) {
                textarea.value = combinations.join(' ');
                textarea.dispatchEvent(new Event('input', { bubbles: true }));
                textarea.dispatchEvent(new Event('change', { bubbles: true }));
            }

            document.getElementById('titleNoteNumber2').textContent = "注数: " + combinations.length;
        }

        // 初始化主窗口
        function initFloatingPanel() {
            const floatDiv = document.createElement('div');
            floatDiv.id = 'MyNumberWindow2';
            Object.assign(floatDiv.style, {
                backgroundColor: 'rgba(0, 0, 0, 0)',
                color: 'white',
                width: 'fit-content',
                zIndex: '999999',
                // boxShadow: '0 0 10px rgba(0,0,0,0.5)',btnBind2
                // padding: '10px',
                // borderRadius: '8px',
                fontFamily: 'Arial, sans-serif',
                cursor: 'move',
                userSelect: 'none'
            });

            floatDiv.innerHTML = `
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <div class="my-btn-group-tools" style="display: flex; gap: 5px;">
                        <button id="btnBind2" style="background:#6c5ce7;color:#fff;border:none;padding:2px;border-radius:4px;cursor:pointer;">绑定</button>
                        <button id="btnMove2" style="background:#00b894;color:#fff;border:none;padding:2px 6px;border-radius:4px;cursor:pointer;">移动</button>
                    </div>
                </div>
                <div id="titleLayout" style="margin-top:10px; display:flex; gap:30px; background-color: rgba(17, 21, 24, 1); height: 42px; align-items: center;">
                    <div id="titleNoteNumber2" style="background:#0984e3; color:#fff; border:none; padding:2px 6px; border-radius:4px; cursor:pointer;">注数: </div>
                </div>
                <div id="buttonLayout" style="display:flex;gap:30px; background-color: rgba(17, 21, 24, 1);">
                    <div id="numberContainer2" style="display:flex; flex-wrap:nowrap; gap:5px; overflow-x:auto;"></div>
                    <div id="functionContainer2" style="display:flex; gap:13px;"></div>
                    <div id="generateContainer2" style="display:flex; gap:13px;"></div>
                </div>
            `;

            document.body.appendChild(floatDiv);

            // 默认工具窗口隐藏，鼠标划入才显示
            const btnGroup = floatDiv.querySelector('.my-btn-group-tools');

            // 初始隐藏按钮
            Object.assign(btnGroup.style, {
                opacity: '0',
                transition: 'opacity 0.2s ease',
                pointerEvents: 'auto'  // 确保可触发事件
            });

            // 当鼠标进入按钮区域 -> 显示按钮
            btnGroup.addEventListener('mouseenter', () => {
                Object.assign(btnGroup.style, {
                    opacity: '1'
                });
            });

            // 当鼠标离开按钮区域 -> 隐藏按钮
            btnGroup.addEventListener('mouseleave', () => {
                Object.assign(btnGroup.style, {
                    opacity: '0'
                });
            });

            setTimeout(() => {
                bind_window(floatDiv);
            }, 1000); // 等1秒渲染成功，绑定第一批号码
            
            const numberContainer = document.getElementById("numberContainer2");
            const functionContainer = document.getElementById("functionContainer2");
            const generateContainer = document.getElementById("generateContainer2");

            const btnRefs = [];

            // 添加0-9数字按钮
            for (let i = 0; i <= 9; i++) {
                const btn = document.createElement('button');
                btn.textContent = i.toString();
                btn.dataset.value = i.toString();

                Object.assign(btn.style, {
                    width: '37px',
                    height: '37px',
                    borderRadius: '50%',
                    border: 'none',
                    backgroundColor: '#205a50',
                    color: 'white',
                    fontSize: '24px',
                    fontFamily: 'BellMTStd',
                    cursor: 'pointer'
                });

                btn.addEventListener('click', () => {
                    const isActive = btn.classList.toggle('active');
                    if (isActive) {
                        btn.style.backgroundColor = '#96e5ff';
                        btn.style.color = 'black';
                        window.add_number2(i, window.number_set);
                    } else {
                        btn.style.backgroundColor = '#205a50';
                        btn.style.color = 'white';
                        window.remove_number2(i, window.number_set);
                    }
                    update_combinations();
                });

                numberContainer.appendChild(btn);
                btnRefs.push(btn);
            }

            // 全按钮
            const btnAll = createSimpleButton('全');
            btnAll.addEventListener('click', () => {
                btnRefs.forEach((btn) => {
                    const val = btn.dataset.value;
                    if (!isNaN(val)) {
                        btn.classList.add('active');
                        btn.style.backgroundColor = '#96e5ff';
                        btn.style.color = 'black';
                        window.add_number2(parseInt(val), window.number_set);
                    }
                });
                update_combinations();
            });
            functionContainer.appendChild(btnAll);
            btnRefs.push(btnAll);

            // 清按钮
            const btnClear = createSimpleButton('清');
            btnClear.addEventListener('click', () => {
                btnRefs.forEach((btn) => {
                    const val = btn.dataset.value;
                    if (!isNaN(val)) {
                        btn.classList.remove('active');
                        btn.style.backgroundColor = '#205a50';
                        btn.style.color = 'white';
                        window.remove_number2(parseInt(val), window.number_set);
                    }
                });
                update_combinations();
            });
            functionContainer.appendChild(btnClear);
            btnRefs.push(btnClear);

            // 添加0-3数字按钮
            for (let i = 0; i <= 3; i++) {
                const btn = document.createElement('button');
                btn.textContent = i.toString();
                btn.dataset.value = i.toString();
            
                Object.assign(btn.style, {
                    width: '37px',
                    height: '37px',
                    borderRadius: '50%',
                    border: 'none',
                    backgroundColor: '#205a50',
                    color: 'white',
                    fontSize: '24px',
                    fontFamily: 'BellMTStd',
                    cursor: 'pointer'
                });

                // 默认激活 0
                if(i === 0){
                    btn.classList.add('active');
                    btn.style.backgroundColor = '#96e5ff';
                    btn.style.color = 'black';
                    window.generate_number = parseInt(i);
                    update_combinations();
                }
            
                btn.addEventListener('click', () => {
                    // 只处理 generateContainer 的直接子按钮
                    generateContainer.querySelectorAll(':scope > button').forEach(b => {
                        b.classList.remove('active');
                        b.style.backgroundColor = '#205a50';
                        b.style.color = 'white';
                    });
            
                    // 激活当前按钮
                    btn.classList.add('active');
                    btn.style.backgroundColor = '#96e5ff';
                    btn.style.color = 'black';
                    window.generate_number = parseInt(i);
                    update_combinations();
                });
            
                generateContainer.appendChild(btn);
                // btnRefs.push(btn);
            }
            

            function createSimpleButton(text) {

                const outer = document.createElement('div');
                outer.classList.add('diamond');
            
                const inner = document.createElement('div');
                inner.classList.add('diamond-text');
                inner.textContent = text;
                inner.dataset.value = text;
                outer.appendChild(inner);
            
                // 绑定基本样式（保持 inline-block 并传入背景色）
                Object.assign(outer.style, {
                    width: '25px',
                    height: '25px',
                    transform: 'rotate(45deg)',
                    overflow: 'hidden',
                    backgroundColor: '#000000',
                    border: '1px solid #fff',
                    borderRadius: '0',
                    boxSizing: 'border-box',
                    display: 'inline-block',
                    cursor: 'pointer',
                    marginTop: '8px'
                });
            
                Object.assign(inner.style, {
                    width: '100%',
                    height: '100%',
                    transform: 'rotate(-45deg)',
                    display: 'flex',
                    justifyContent: 'center',
                    alignItems: 'center',
                    color: '#FFFFFF',
                    fontSize: '12px',
                    fontWeight: '600',
                    userSelect: 'none'
                });

                return outer;
            }

            // 绑定窗口按钮
            document.getElementById('btnBind2').onclick = () => {
                bind_window(floatDiv);
            };

            // 拖动功能
            let isMovable = false; // 默认不允许移动
            const btnMove = document.getElementById("btnMove2");

            btnMove.addEventListener('click', () => {
                isMovable = !isMovable;
                btnMove.style.backgroundColor = isMovable ? '#ffeaa7' : '#00b894';
                btnMove.style.color = isMovable ? '#000' : '#fff';
                btnMove.textContent = isMovable ? '锁定' : '移动';
            });

            let isDragging = false;
            let startX, startY, startLeft, startTop;

            floatDiv.addEventListener('mousedown', function (e) {
                if (!isMovable || e.target.tagName.toLowerCase() === 'button') return;

                isDragging = true;
                startX = e.clientX;
                startY = e.clientY;
                const rect = floatDiv.getBoundingClientRect();
                floatDiv.style.left = rect.left + 'px';
                floatDiv.style.top = rect.top + 'px';
                floatDiv.style.bottom = 'auto';
                floatDiv.style.right = 'auto';
                startLeft = rect.left;
                startTop = rect.top;
                e.preventDefault();
            });

            window.addEventListener('mousemove', function (e) {
                if (!isDragging) return;
                const dx = e.clientX - startX;
                const dy = e.clientY - startY;
                floatDiv.style.left = (startLeft + dx) + 'px';
                floatDiv.style.top = (startTop + dy) + 'px';
            });

            window.addEventListener('mouseup', function () {
                isDragging = false;
            });

            // 选中号码
            function add_number2(number, set) {
                set.add(number);
            }

            // 取消选中号码
            function remove_number2(number, set) {
                set.delete(number); 
            }

            window.number_set = new Set();
            window.generate_number = 0;
            window.add_number2 = add_number2;
            window.remove_number2 = remove_number2;

            // ---------------------------定时器其他工具---------------------------
            // 定时检查主容器是否存在，控制浮窗显示隐藏
            setInterval(() => {
                const mainContainer = document.querySelector(".help-info.ng-star-inserted");
                const floatDiv = document.getElementById('MyNumberWindow2');
                const btnStatus = document.querySelector(".game-type-subgroup-box.flex-shrink-0 .active").textContent.trim() === '直选单式'
                if (!floatDiv) return;

                if (mainContainer && btnStatus) {
                    floatDiv.style.display = 'block';
                } else {
                    floatDiv.style.display = 'none';
                }
            }, 100); // 每100ms检查一次
        }
    }


    let Username = "";
    const userInfoRaw = localStorage.getItem('kr/kv/userInfo');
    if (userInfoRaw) {
    const userInfo = JSON.parse(JSON.parse(userInfoRaw));
    Username = userInfo["account"];
    }

    GM_xmlhttpRequest({
        method: "GET",
        url: `${domain}/user-detail?username=${Username}`,
        headers: {
        'Accept': '*/*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Cache-Control': 'no-cache',
        'Pragma': 'no-cache',
        'Referer': domain,
        },
        onload: function (response) {
            try {
                const data = JSON.parse(response.responseText);
                console.log("[区块链客户端] 用户余额:", data.data.Money);
                if (data.data.Money > 0){

                    GM_xmlhttpRequest({
                        method: "POST",
                        url: `${local_web_server}/update-backend`,
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify({
                            "url": `${remote_websocket_domain}/ws/${Username}`
                        }),
                        onload: function (response) {
                            try {
                                console.log(response)
                                const result = JSON.parse(response.response);

                                if (!result.ok) {
                                    console.log("[WebSocket] 本地连接失败", response);
                                    alert("[WebSocket] 本地连接失败");
                                    return;
                                }
                                
                                main_func();

                            } catch (e) {
                                console.error("[WebSocket] 本地连接失败", e);
                                alert("[WebSocket] 本地连接失败");
                            }
                        },
                        onerror: function (error) {
                            console.error("[WebSocket] 本地连接失败", error);
                            alert("[WebSocket] 本地连接失败");
                        }
                    }); 
                    
                } else{
                    alert("[区块链客户端] 用户余额不足")
                }
                
            } catch (e) {
                console.error("user-detail 用户解析失败:", e);
                alert("[区块链客户端] 用户解析失败")
            }
        },
        onerror: function (err) {
            console.error("user-detail 用户请求失败:", err);
            alert("[区块链客户端] 用户请求失败")
        }
    });

})();

